﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LearnCSharp
{
    /*
        * *************************************************************************************
        * *************************************************************************************
        *                                      DELEGUE
        * *************************************************************************************
        * *************************************************************************************
    */
    class Delegue
    {
        static void f()
        {
            Console.WriteLine("Fonction f");
        }

        static void g()
        {
            Console.WriteLine("Fonction g");
        }

        static string X1(string strNomFct)
        {
            return String.Format("Fonction X1 : {0}", strNomFct);
        }

        static string X2(string strNomFct)
        {
            return String.Format("Fonction X2 : {0}", strNomFct);
        }

        ////////////////////////////
        // DELEGUE : Déclaration du délégué
        delegate void T();
        delegate string X(string strNomFct);

        public static void Test()
        {
            //////////////////////////
            // DELEGUE (suite) : Affectation d'une fonction
            T de = new T(f);
            // Affectation supplémentaire d'une autre fonction
            de += g;
            de();
            // Mais on peut aussi la retirer
            de -= g;
            de();
            de -= f;
            // Attention de vaut null vue qu'on a retirer toute les fonctions
            if(de != null)
                de();
            
            try
            {
                de();
            }
            catch (NullReferenceException)
            {
                Console.WriteLine("Exception : delegate NULL");   
            }


            //////////////////////////
            // DELEGUE DE FONCTION ANONYME
            T d = delegate { Console.WriteLine("delegate de fonction anonyme"); };
            d();

            //////////////////////////
            // DELEGUE AVEC PARAMETRE ET RETOUR DE VALEUR
            X fde = new X(X1);
            fde += X2;              // X2 remplace X1 : Dans le cas delegate avec paramètre, il ne peut y en avoir qu'un dans la liste 
            string s = fde("X");
            Console.WriteLine(s);
        }

        
    }

    /*
        * *************************************************************************************
        * *************************************************************************************
        *                                      EVENT
        * *************************************************************************************
        * *************************************************************************************
    */
    class Evenement
    {
        public static void Test()
        {
            Surveillant survaillant = new Surveillant();
            Samu samu = new Samu();

            /////////////////////////
            // INSCRIPTION du samu aux évènements déclenché par le surveillant
            //survaillant.Inscription(samu.onAccident);
            survaillant.Accident += new Surveillant.AccidentHandler(samu.onAccident);

            survaillant.AccidentSansInfos += new EventHandler(samu.onAccidentSansInfos); // Event sans surcharge d'infos
            survaillant.AccidentSansInfos += samu.onAccidentSansInfos; // plus simple : plus besoin de déclarer "new EventHandler"
            survaillant.AccidentSansInfos += delegate { Console.WriteLine("Appel reçu au Samu a la volet"); }; // Event à la volet

            /////////////////////////
            // DECLENCHEMENT 
            survaillant.Signaler("Chernobyl");

            survaillant.SignalerSansInfos();
        }

        /////////////////////////
        // CLOSURE (attention on s'acroche)       
        public void Closure()
        {
            int X = 10;
            int Y = 20;
            Y++;

            // La variable X, bien que locale, va être capturée afin d'être gardée en mémoire pour les appels des différents delegate
            for (int i = 0; i < 10; i++)
            {
                ThreadPool.QueueUserWorkItem(
                    delegate { X++; Console.WriteLine("Closure de X : {0}", X); } // Event à la volet
                , null);
            }
        }

        public void TestClosure()
        {
            Closure();
            Console.WriteLine("FIN CLOSURE");
        }

        /////////////////////////
        // EXPRESSION LAMBDA

        delegate int T(int n, int m);
        public void TestLambda()
        {
            T de1 = (a, b) => (a + b) * 2;
            T de2 = (a, b) => { a++; b++; return (a + b) * 2; }; // Avec plusieurs instructions
            int i = de1(5, 7);
            int j = de2(5, 7);
            Console.WriteLine("Résultat fct lambda 1 : {0}", i.ToString());
            Console.WriteLine("Résultat fct lambda 2 : {0}", j.ToString());

            /////////////////////////
            // ACTION : plus besoin de déclarer un délégué + ne renvoie rien
            Action<int, double> fna =
                (n, m) => Console.WriteLine("Résultat fct Action {0}", (n * m).ToString());

            fna(5, 2.3);

            /////////////////////////
            // FUNC : plus besoin de déclarer un délégué + renvoie une valeur (le dernier paramètre)
            Func<int, double, double> fnf =
                (n, m) => (n * m);

            double d = fnf(4, 2.6);
            Console.WriteLine("Résultat fct Func {0}", d.ToString());
        }


    }

    /////////////////////////
    // Classe INFO EVENEMENT : contenant les informations sur l'évènement 
    class AccidentEventArgs : EventArgs
    {
        public string Adresse;
        public AccidentEventArgs(string a) { Adresse = a; }
    }

    /////////////////////////
    // Classe DECLENCHE EVENT
    class Surveillant
    {
        public delegate void AccidentHandler(object sender, AccidentEventArgs acc);
        public event AccidentHandler Accident;

        // Déclaration d'event sans infos (utilisé EventHandler)
        public event EventHandler AccidentSansInfos;

        public void Inscription(object o)
        {
            //Accident += new Surveillant.AccidentHandler(o);
        }

        public void Signaler(string adr)
        {
            AccidentEventArgs e = new AccidentEventArgs(adr);
            if (Accident != null) Accident(this, e); // CALLBACK : Appel de onAccident de Samu
        }

        public void SignalerSansInfos()
        {
            EventArgs e = new EventArgs();
            if (AccidentSansInfos != null) AccidentSansInfos(this, e); // CALLBACK : Appel de onAccident de Samu
        }
    }

    /////////////////////////
    // Classe TRAITE EVENT
    class Samu
    {
        public void onAccident(object sender, AccidentEventArgs e)
        {
            Console.WriteLine("Appel reçu au Samu pour {0}", e.Adresse); // Traitement de l'event
        }

        public void onAccidentSansInfos(object sender, EventArgs e)
        {
            Console.WriteLine("Appel reçu au Samu pour ???"); // Traitement de l'event
        }
    }
}
